/*
 * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi/native_api.h"
#include "napi/native_common.h"
#include "napi/native_node_api.h"

#include "nlohmann/json.hpp"
#include <memory>
#include <string>
#include <mutex>
#include <uv.h>

#include "device_manager_callback.h"
#include "dm_app_image_info.h"
#include "dm_device_info.h"
#include "dm_subscribe_info.h"
#include "dm_publish_info.h"
#include "dm_anonymous.h"
#include "dm_error_message.h"
#include "napi_file_share_event.h"
#include "file_share_log.h"
#include "i_file_share_callback.h"
#include "file_share_common.h"

namespace OHOS {
namespace FileShare {
struct FSNapiPublishJsCallback {
    std::string bundleName_;
    int32_t publishId_;
    int32_t reason_;

    FSNapiPublishJsCallback(std::string bundleName, int32_t publishId, int32_t reason)
        : bundleName_(bundleName), publishId_(publishId), reason_(reason) {}
};

struct FSNapiStatusJsCallback {
    std::string bundleName_;
    uint16_t subscribeId_;
    int32_t reason_;
    OHOS::DistributedHardware::DmDeviceBasicInfo deviceBasicInfo_;

    FSNapiStatusJsCallback(std::string bundleName, uint16_t subscribeId, int32_t reason,
                           OHOS::DistributedHardware::DmDeviceBasicInfo deviceBasicInfo_)
        : bundleName_(bundleName), subscribeId_(subscribeId), reason_(reason), deviceBasicInfo_(deviceBasicInfo_) {}
};

struct AuthAsyncCallbackInfo {
    napi_env env = nullptr;

    char bundleName[FS_NAPI_BUF_LENGTH] = {0};
    size_t bundleNameLen = 0;

    napi_ref callback = nullptr;
    int32_t authType = -1;
};

struct FSNapiAuthJsCallback {
    std::string bundleName_;
    std::string deviceId_;
    std::string token_;
    int32_t status_;
    int32_t reason_;

    FSNapiAuthJsCallback(std::string bundleName, std::string deviceId, std::string token, int32_t status,
        int32_t reason)
        : bundleName_(bundleName), deviceId_(deviceId), token_(token), status_(status), reason_(reason) {}
};

struct FSNapiSendFileJSCallback {
    std::string bundleName_;
    SendState state_;
    SendInfo info_;

    FSNapiSendFileJSCallback(std::string bundleName, SendState state, SendInfo info)
        : bundleName_(bundleName), state_(state), info_(info) {}
};

struct FSNapiUIJsCallback {
    std::string bundleName_;
    std::string paramJson;

    FSNapiUIJsCallback(std::string bundleName, std::string paramJson)
        : bundleName_(bundleName), paramJson(paramJson) {}
};

struct FSNapiShareStatusJSCallback {
    std::string bundleName_;
    bool shareStatus_;

    FSNapiShareStatusJSCallback(std::string bundleName, bool ShareStatus)
        : bundleName_(bundleName), shareStatus_(ShareStatus) {}
};

enum FSNapiDevStatusChange { 
    UNKNOWN = 0, 
    AVAILABLE = 1, 
    UNAVAILABLE = 2, 
    CHANGE = 3
};

enum FSNapiUiAction { 
    CONFIRM_CONNECT = 0,
    REFUSE_CONNECT = 1,
};

class NapiFileShare : public FSNativeEvent {
public:
    explicit NapiFileShare(napi_env env, napi_value thisVar);
    ~NapiFileShare() override;
    static napi_value EnableShare(napi_env env, napi_callback_info info);
    static napi_value DisableShare(napi_env env, napi_callback_info info);
    static napi_value GetShareStatus(napi_env env, napi_callback_info info);
    static napi_value StartScan(napi_env env, napi_callback_info info);
    static napi_value StopScan(napi_env env, napi_callback_info info);
    static napi_value Connect(napi_env env, napi_callback_info info);
    static napi_value Disconnect(napi_env env, napi_callback_info info);
    static napi_value ConfirmConnect(napi_env env, napi_callback_info info);
    static napi_value RefuseConnect(napi_env env, napi_callback_info info);
    static napi_value JsOn(napi_env env, napi_callback_info info);
    static napi_value JsOff(napi_env env, napi_callback_info info);
    static NapiFileShare *GetFileShareNapi(const std::string &bundleName);
    void OnPublishResult(int32_t publishId, int32_t publishResult);
    void OnDiscoveryFailed(uint16_t subscribeId, int32_t failedReason);
    void OnDeviceFound(uint16_t subscribeId, const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo);
    void OnAuthResult(const std::string &deviceId, const std::string &token, int32_t status, int32_t reason);
    void OnDeviceStatusChange(FSNapiDevStatusChange action,
                             const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo);   
    void OnSending(SendState state, SendInfo &info);               
    void OnUiCall(const std::string &paramJson);
    void OnShareStatus(bool shareStatus);
    static napi_value CreateShareManager(napi_env env, napi_callback_info info);
    static napi_value ReleaseShareManager(napi_env env, napi_callback_info info);     
    static napi_value Constructor(napi_env env, napi_callback_info info);
    static napi_value SendFile(napi_env env, napi_callback_info info);
    static napi_value CancelSendFile(napi_env env, napi_callback_info info);
    static napi_value AbortReceiveFile(napi_env env, napi_callback_info info);
    static napi_value GetShareStoragePath(napi_env env, napi_callback_info info);
    static napi_value SetShareStoragePath(napi_env env, napi_callback_info info);
    static napi_value GetShareOpenTime(napi_env env, napi_callback_info info);
    static napi_value SetShareOpenTime(napi_env env, napi_callback_info info);
    static napi_value GetAvailableDeviceList(napi_env env, napi_callback_info info);

    static thread_local napi_ref sConstructor;       

private:
    static void BindDevice(NapiFileShare *shareManagerWrapper, const std::string &deviceId, napi_env env, napi_value &object);
    static napi_value JsOnProcess(napi_env env, int32_t num, napi_value thisVar, napi_value argv[]);
    static napi_value JsOffProcess(napi_env env, int32_t num, napi_value thisVar, napi_value argv[]);
    static void CreateFSCallback(napi_env env, const std::string &bundleName, std::string &eventType);
    static void ReleaseFSCallback(std::string &bundleName, std::string &eventType);
    static napi_value SendFileInner(napi_env env, ShareType type, std::string &deviceId, std::vector<FSNapiFileInfo> &fsFiles);
    static std::string FSfilesToFiles(ShareType type, std::vector<FSNapiFileInfo> &fsFiles, std::vector<FileInfo> &files);

    napi_env env_;
    std::string bundleName_;
    static AuthAsyncCallbackInfo authAsyncCallbackInfo_;
};

class FSNapiPublishCallback : public OHOS::DistributedHardware::PublishCallback {
public:
    explicit FSNapiPublishCallback(napi_env env, const std::string &bundleName)
        : env_(env), refCount_(0), bundleName_(bundleName)
    {
    }
    ~FSNapiPublishCallback() override {};
    void OnPublishResult(int32_t publishId, int32_t publishResult) override;
    void IncreaseRefCount();
    void DecreaseRefCount();
    int32_t GetRefCount();

private:
    napi_env env_;
    std::atomic<int32_t> refCount_;
    std::string bundleName_;
};

class FSNapiDiscoveryCallback : public OHOS::DistributedHardware::DiscoveryCallback {
public:
    explicit FSNapiDiscoveryCallback(napi_env env, const std::string &bundleName)
        : env_(env), refCount_(0), bundleName_(bundleName)
    {
    }
    ~FSNapiDiscoveryCallback() override {};
    void OnDeviceFound(uint16_t subscribeId,
                       const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo) override;
    void OnDiscoveryFailed(uint16_t subscribeId, int32_t failedReason) override;
    void OnDiscoverySuccess(uint16_t subscribeId) override;
    void IncreaseRefCount();
    void DecreaseRefCount();
    int32_t GetRefCount();

private:
    napi_env env_;
    std::atomic<int32_t> refCount_;
    const std::string bundleName_;
};

class FSNapiInitCallback : public OHOS::DistributedHardware::DmInitCallback {
public:
    explicit FSNapiInitCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiInitCallback() override {}
    void OnRemoteDied() override;

private:
    napi_env env_;
    std::string bundleName_;
};

class FSNapiAuthenticateCallback : public OHOS::DistributedHardware::AuthenticateCallback {
public:
    explicit FSNapiAuthenticateCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiAuthenticateCallback() override {};
    void OnAuthResult(const std::string &deviceId, const std::string &token, int32_t status, int32_t reason) override;

private:
    napi_env env_;
    std::string bundleName_;
};

class FSNapiDeviceStatusCallback : public OHOS::DistributedHardware::DeviceStatusCallback {
public:
    explicit FSNapiDeviceStatusCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiDeviceStatusCallback() override {};
    void OnDeviceOnline(const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo) override;
    void OnDeviceReady(const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo) override;
    void OnDeviceOffline(const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo) override;
    void OnDeviceChanged(const OHOS::DistributedHardware::DmDeviceBasicInfo &deviceBasicInfo) override;
private:
    napi_env env_;
    std::string bundleName_;
};

class FSNapiSendFileCallback : public SendFileCallback {
public:
    explicit FSNapiSendFileCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiSendFileCallback() override {};
    void OnSending(SendState state, SendInfo &info) override;

private:
    napi_env env_;
    std::string bundleName_;
};

class FSNapiUiCallback : public OHOS::DistributedHardware::DeviceManagerUiCallback {
public:
    explicit FSNapiUiCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiUiCallback() override {};
    void OnCall(const std::string &paramJson) override;

private:
    napi_env env_;
    std::string bundleName_;
};

class FSNapiShareStatusCallback : public ShareStatusCallback {
public:
    explicit FSNapiShareStatusCallback(napi_env env, const std::string &bundleName) : env_(env), bundleName_(bundleName)
    {
    }
    ~FSNapiShareStatusCallback() override {};
    void OnShareStatus(bool shareStatus) override;

private:
    napi_env env_;
    std::string bundleName_;
};
} // namespace FileShare
} // namespace OHOS